#include "Epoller.hpp"
#include <iostream>
#include <memory>

using namespace std;

const uint16_t port = 8888;
const int MAXNUM = 64;

const uint32_t EVENT_IN = EPOLLIN;
const uint32_t EVENT_OUT = EPOLLOUT;

class EpollerServer:public nocopy
{
public:
    EpollerServer()
    :_listensocket_ptr(new Socket()),
     _epoller_ptr(new Epoller())
    {
        ;
    }

    void Init()
    {
        _listensocket_ptr->Bind(port);
        _listensocket_ptr->Listen();
    }

    void Start()
    {
        _epoller_ptr->EpollCtl(EPOLL_CTL_ADD,_listensocket_ptr->_sockfd,EVENT_IN);
        while(1)
        {
            int n = _epoller_ptr->EpollerWait(_revents,MAXNUM);
            if(n > 0)
            {
                lg(Info,"%d events is ready",n);
                Dispatcher(n);
            }
            else if(n == 0)
            {
                lg(Info,"No events is ready");
                continue;
            }
            else
            {
                lg(Error,"Wait error occurs: %s",strerror(errno));
                break;
            }
        }
    }

    void Recver(int fd)
    {
        char buffer[1024];
        int n = read(fd,buffer,sizeof(buffer));
        if(n > 0)
        {
            buffer[n] = 0;
            cout << fd << " send: " << buffer << endl;

            string echo(buffer);
            echo = "echo client$$ " + echo;
            write(fd,echo.c_str(),echo.size());
        }
        else if(n == 0)
        {
            lg(Info,"client out,me too,fd: %d",fd);
            //在epoll模型的红黑树中删除fd必须要是有效的fd，所以要先删除再关闭
            _epoller_ptr->EpollCtl(EPOLL_CTL_DEL,fd,0);
            close(fd);
        }
        else
        {
            lg(Error,"read error,quit fd: %d",fd);
            _epoller_ptr->EpollCtl(EPOLL_CTL_DEL,fd,0);
            close(fd);
        }
    }

    void Accepter()
    {
        string CIp;
        uint16_t CPort;
        int fd = _listensocket_ptr->Accept(&CIp,&CPort);
        if(fd >= 0)
        {
            lg(Info,"%d is added to the epoll mode",fd);
            _epoller_ptr->EpollCtl(EPOLL_CTL_ADD,fd,EVENT_IN);
        }
    }

    void Dispatcher(int n)
    {
        for(int i = 0;i < n;i++)
        {
            if(_revents[i].events & EPOLLIN)
            {
                if(_revents[i].data.fd == _listensocket_ptr->_sockfd)
                {
                    Accepter();
                }
                else
                {
                    //之前的epoll_ctl中为什么传入了fd后，还要在events结构体中添加fd？？？
                    //这是因为在从就绪队列拿出来的是底层反馈给你的events，只有在这里面放入了fd
                    //才能直接找到是哪个文件描述符就绪了
                    Recver(_revents[i].data.fd);
                }
            }
            else if(_revents[i].events & EPOLLOUT)
            {

            }
            else
            {

            }
        }
    }

    ~EpollerServer()
    {
        _listensocket_ptr->Close();
    }

private:
    shared_ptr<Socket> _listensocket_ptr;
    shared_ptr<Epoller> _epoller_ptr;
    struct epoll_event _revents[MAXNUM];
};